import json
import os
import shutil
import sys
import threading
import tkinter as tk
import tkinter.ttk as ttk
import winreg
import zipfile
from tkinter import messagebox

from machine.calendar_date import Calendar
from machine.generate_cert import generate_cert
from machine.generate_key import generate_key, read_public_key, generate_sign_code, public_key_path, private_key_path, ja_netfilter_path
from machine.machine_log import logger

# 定义全局变量 loading_window
loading_window = None


# 初始化窗口
def init_window(root, width=None, height=None):
    """
    使窗口居中显示
    :param root: 主窗口实例
    :param width: 窗口宽度，默认为None，此时会自动获取窗口的实际宽度
    :param height: 窗口高度，默认为None，此时会自动获取窗口的实际高度
    """
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight() \
        # 禁用窗口的放大和缩小功能，参数分别对应宽度和高度的调整，False表示禁止调整
    root.resizable(False, False)
    # 如果未给出宽度和高度，则获取窗口自身的请求宽度和高度
    if width is None:
        width = root.winfo_reqwidth()
    if height is None:
        height = root.winfo_reqheight()

    # 计算窗口左上角的x和y坐标，使其居中
    x_cord_innate = int((screen_width / 2) - (width / 2))
    y_cord_innate = int((screen_height / 2) - (height / 2))

    # 设置窗口的几何属性（位置和大小）
    root.geometry(f'{width}x{height}+{x_cord_innate}+{y_cord_innate}')


# 获取授权时间
def on_time_entry_click(self, time_str):
    root = self.root

    [time_str.set(date)
     for date in [Calendar((root.winfo_pointerx(), root.winfo_pointery()), 'lr').selection()]
     if date]
    on_gen_combobox_change(self)


# 获取产品配置
def get_product_option():
    # 指定要读取的目录路径
    directory = resource_path('resources/product')
    # 列出目录中的所有文件和子目录
    files_and_directories = os.listdir(directory)
    # 过滤出文件（排除子目录）
    files = [f for f in files_and_directories if os.path.isfile(os.path.join(directory, f))]
    return files


def on_gen_combobox_change(self):
    # 检查插件路径是否存在
    if os.path.exists(ja_netfilter_path):
        self.install_btn.config(state=[tk.DISABLED])
        self.uninstall_btn.config(state=[tk.NORMAL])
    else:
        self.install_btn.config(state=[tk.NORMAL])
        self.uninstall_btn.config(state=[tk.DISABLED])
        self.gen_btn.config(state=[tk.DISABLED])
        messagebox.showwarning('', '插件不存在，请安装插件！')
        return

    # 检查证书是否存在
    if not os.path.exists(public_key_path) or not os.path.exists(private_key_path):
        messagebox.showwarning('', '证书不存在，请生成证书！')
        return

    sign_text = self.sign_text
    code_text = self.code_text
    combobox = self.gen_combobox
    person_entry = self.person_entry
    time_entry = self.time_entry

    product_name = combobox.get()
    product_file = resource_path(f'resources\\product\\{product_name}.json')
    with open(product_file, 'r', encoding='utf-8') as file:
        license_json = json.load(file)
    public_key_base64 = read_public_key()
    sign_code = generate_sign_code(public_key_base64)

    auth_person = person_entry.get().strip()
    auth_time = time_entry.get().strip()
    if len(auth_person) > 0:
        license_json['licenseeName'] = auth_person
    if len(auth_time) > 0:
        for product in license_json['products']:
            product['paidUpTo'] = auth_time
            product['fallbackDate'] = auth_time
    license_code = generate_key(public_key_base64, license_json)

    # 清除 Text 小部件中的所有内容
    sign_text.delete('1.0', tk.END)
    # 定义一个标签并设置其左边距和上边距
    sign_text.tag_configure('padding', lmargin1=5, lmargin2=5, spacing1=5, spacing3=5)
    # 插入带有标签的文本
    sign_text.insert(tk.END, sign_code, 'padding')

    # 清除 Text 小部件中的所有内容
    code_text.delete('1.0', tk.END)
    # 定义一个标签并设置其左边距和上边距
    code_text.tag_configure('padding', lmargin1=5, lmargin2=5, spacing1=5, spacing3=5)
    # 插入带有标签的文本
    code_text.insert(tk.END, license_code, 'padding')
    return sign_code


def write_sign(sign_code):
    # 以覆盖的形式写入数据到 power.conf 文件
    with open(f'{ja_netfilter_path}\\config-jetbrains\\power.conf', 'w') as file:
        file.write(f'[Result]\n{sign_code}')


def check_btn_disabled(btn):
    # 检查按钮状态
    return btn.instate(['disabled', '!disabled'])


# 生成证书
def on_gen_btn_click(self):
    btn = self.gen_btn

    # 检查按钮状态
    if check_btn_disabled(btn):
        logger.warn('gen_btn 按钮被禁用，禁止点击')
        return

    btn.config(state=[tk.DISABLED])
    # 检查插件是否安装
    jar_file = f'{ja_netfilter_path}\\ja-netfilter.jar'
    if not os.path.exists(jar_file):
        messagebox.showwarning('', '请先安装插件！')
        btn.config(state=tk.NORMAL)
        return

    try:
        if os.path.exists(public_key_path) and os.path.exists(private_key_path):
            is_ok = messagebox.askokcancel('', '证书已存在，是否生成新证书？')
            if not is_ok:
                return
        # 生成证书
        generate_cert()
        # 加载证书生成新的激活码
        sign_code = on_gen_combobox_change(self)
        # 更新签名
        write_sign(sign_code)

        messagebox.showinfo('', '生成成功！')
    except Exception as e:
        messagebox.showerror('', '生成失败！')
    finally:
        btn.config(state=tk.NORMAL)


def check_environment_variable(variable_name):
    """
    检查指定的环境变量是否存在。

    :param variable_name: 要检查的环境变量名称
    :return: 如果环境变量存在，返回 True；否则返回 False
    """
    return variable_name in os.environ


def set_environment_variable(variables):
    try:
        # 打开系统环境变量键
        # key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0, winreg.KEY_ALL_ACCESS)
        # 打开用户环境变量键
        reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0, winreg.KEY_ALL_ACCESS)
        # 尝试设置环境变量
        for key, value in variables.items():
            try:
                winreg.SetValueEx(reg_key, key, 0, winreg.REG_EXPAND_SZ, value)
                logger.info(f"设置环境变量成功: {key} = {value}")
            except FileNotFoundError as e:
                logger.error(f"环境变量 {key} 不存在: {e}")
            except Exception as e:
                logger.error(f"设置环境变量 {key} 时出错: {e}")
        # 刷新环境变量
        from ctypes import windll
        # 关闭键
        winreg.CloseKey(reg_key)

        # 通知系统环境变量已更改
        windll.user32.SendMessageTimeoutW(
            0xFFFF,  # HWND_BROADCAST
            0x1A,  # WM_SETTINGCHANGE
            0,
            "Environment",
            0x0002,  # SMTO_ABORTIFHUNG
            5000,  # 超时时间（毫秒）
            None
        )
    except Exception as e:
        logger.error(f"设置环境变量时出错: {e}")


def delete_environment_variable(variables):
    try:
        # 打开系统环境变量键
        # key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0, winreg.KEY_ALL_ACCESS)
        # 打开用户环境变量键
        reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0, winreg.KEY_ALL_ACCESS)
        # 尝试删除环境变量
        for key in variables:
            try:
                winreg.DeleteValue(reg_key, key)
                logger.info(f"删除环境变量成功: {key}")
            except FileNotFoundError as e:
                logger.error(f"环境变量 {key} 不存在: {e}")
            except Exception as e:
                logger.error(f"删除环境变量 {key} 时出错: {e}")

        # 刷新环境变量
        from ctypes import windll
        # 关闭键
        winreg.CloseKey(reg_key)
        # 通知系统环境变量已更改
        windll.user32.SendMessageTimeoutW(
            0xFFFF,  # HWND_BROADCAST
            0x1A,  # WM_SETTINGCHANGE
            0,
            "Environment",
            0x0002,  # SMTO_ABORTIFHUNG
            5000,  # 超时时间（毫秒）
            None
        )
    except Exception as e:
        logger.error(f"删除环境变量时出错: {e}")


def delete_folder(folder_path):
    try:
        # 使用 shutil.rmtree 递归地删除文件夹及其内容
        shutil.rmtree(folder_path)
        logger.info(f"成功删除文件夹: {folder_path}")
    except FileNotFoundError:
        logger.error(f"文件夹不存在: {folder_path}")
    except PermissionError:
        logger.error(f"权限错误: 无法删除文件夹 {folder_path}")
    except Exception as e:
        logger.error(f"删除文件夹时出错: {e}")


def env_var_opt(opt):
    user_home = r'C:\Users\Public'
    target_dir = f'{user_home}\\.ja-netfilter'
    # 移除环境变量
    env_var_path = resource_path('resources/env_var.json')
    with open(env_var_path, 'r', encoding='utf-8') as file:
        env_var_json = json.load(file)
    env_var_path_dict = {}
    for product in env_var_json:
        env_var_path_dict[f'{product.upper()}_VM_OPTIONS'] = f'{target_dir}\\vmoptions\\{product}.vmoptions'
    if opt:
        set_environment_variable(env_var_path_dict)
    else:
        delete_environment_variable(env_var_path_dict)


def plugin_opt(opt):
    ja_netfilter = resource_path('resources/ja-netfilter.zip')
    if not opt:
        delete_folder(ja_netfilter_path)
        return

    # 确保目标目录存在
    os.makedirs(ja_netfilter_path, exist_ok=True)
    # 定义目标 ZIP 文件路径
    target_zip_path = f'{ja_netfilter_path}\\ja-netfilter.zip'
    # 读取资源文件并写入目标目录
    shutil.copy(ja_netfilter.__str__(), target_zip_path)
    # 解压 ZIP 文件
    with zipfile.ZipFile(target_zip_path, 'r') as zip_ref:
        zip_ref.extractall(ja_netfilter_path)
    logger.info(f"文件已成功复制并解压到 {ja_netfilter_path}")


def long_running_task(self):
    # 模拟耗时操作，线程睡眠 5 秒
    # 添加环境变量
    env_var_opt(True)
    # 解压文件
    plugin_opt(True)
    # 耗时操作完成后，关闭加载框并显示消息
    loading_window.destroy()
    messagebox.showinfo("", "安装完成")

    # 启用按钮
    self.uninstall_btn.config(state=tk.NORMAL)
    self.gen_btn.config(state=tk.NORMAL)


def loading_center_window(child, parent):
    # 获取父窗口的位置和大小
    parent_x = parent.winfo_x()
    parent_y = parent.winfo_y()
    parent_width = parent.winfo_width()
    parent_height = parent.winfo_height()

    # 获取子窗口的大小
    child_width = child.winfo_reqwidth()
    child_height = child.winfo_reqheight()

    # 计算子窗口的位置
    x = parent_x + (parent_width // 2) - (child_width // 2)
    y = parent_y + (parent_height // 2) - (child_height // 2)

    # 设置子窗口的位置
    child.geometry(f"+{x}+{y}")


def start_long_running_task(self):
    global loading_window
    root = self.root
    # 创建加载框
    loading_window = tk.Toplevel(root)
    loading_window.title("")
    loading_window.geometry("200x80")

    # 设置加载框始终置顶
    loading_window.attributes('-topmost', True)
    # 禁用关闭按钮
    loading_window.protocol("WM_DELETE_WINDOW", lambda: None)
    # 禁用最大化和最小化按钮
    loading_window.resizable(False, False)
    # loading_window.overrideredirect(True)  # 隐藏标题栏和边框

    # 使加载框相对于父窗口居中
    loading_center_window(loading_window, root)

    # 在加载框中添加一个标签
    label = tk.Label(loading_window, text="安装中请稍候...")
    label.pack(pady=20)

    # 启动耗时操作的线程
    thread = threading.Thread(target=long_running_task(self))
    thread.start()


def on_install_btn_click(self):
    logger.info('on_install_btn_click')
    # 检查按钮状态
    if check_btn_disabled(self.install_btn):
        logger.warn('install_btn 按钮被禁用，禁止点击')
        return
    start_long_running_task(self)


def on_uninstall_btn_click(self):
    logger.info('on_uninstall_btn_click')
    btn = self.uninstall_btn
    # 检查按钮状态
    if check_btn_disabled(btn):
        logger.warn('uninstall_btn 按钮被禁用，禁止点击')
        return

    btn.config(state=[tk.DISABLED])
    try:
        # 删除环境变量
        env_var_opt(False)
        # 删除文件
        plugin_opt(False)
        messagebox.showinfo("", "卸载完成")

        # 禁用按钮
        self.install_btn.config(state=tk.NORMAL)
        self.uninstall_btn.config(state=tk.DISABLED)
        self.gen_btn.config(state=tk.DISABLED)

        # 清除 Text 小部件中的所有内容
        self.sign_text.delete('1.0', tk.END)
        self.code_text.delete('1.0', tk.END)

    except Exception as e:
        logger.error(e)
    finally:
        btn.config(state=[tk.NORMAL])


def on_sign_btn_click(self):
    root = self.root
    sign_text = self.sign_text

    # 获取内容
    content = sign_text.get('1.0', tk.END).replace('\n', '')
    if not content:
        messagebox.showwarning('', '签名为空，不能复制！')
        return
    # 全选文本
    on_text_select_all_focus(sign_text)
    # 清空剪切板
    root.clipboard_clear()
    # 将内容添加到剪切板
    root.clipboard_append(content)
    # 提示用户已复制
    # messagebox.showinfo('信息', '复制成功')


def on_code_btn_click(self):
    root = self.root
    code_text = self.code_text

    # 获取内容
    content = code_text.get('1.0', tk.END).replace('\n', '')
    if not content:
        messagebox.showwarning('', '激活码为空，不能复制！')
        return

    # 全选文本
    on_text_select_all_focus(code_text)
    # 清空剪切板
    root.clipboard_clear()
    # 将内容添加到剪切板
    root.clipboard_append(content)
    # 提示用户已复制
    # messagebox.showinfo('信息', '复制成功')


def on_text_select_all_focus(text):
    logger.info('on_sign_text_click')
    # 全选 Text 小部件中的所有文本
    text.tag_add(tk.SEL, '1.0', tk.END)
    # 确保 Text 小部件获得焦点
    text.focus_set()
    # 将插入点设置到文本的开始位置
    text.mark_set(tk.INSERT, '1.0')
    # 确保插入点可见
    text.see(tk.INSERT)


def on_key_press(event):
    # 检查是否是删除键（Backspace 或 Delete）
    if event.keysym in ('BackSpace', 'Delete'):
        return 'break'  # 阻止删除操作
    # 其他按键可以正常处理
    return None


# 定义事件处理函数
def on_disable_key_press(event):
    # 阻止所有按键输入
    return "break"


def on_person_entry_key_press(self):
    person_entry = self.person_entry
    # 获取当前文本内容
    current_content = person_entry.get()

    # 检查当前文本长度（减去换行符）
    if len(current_content.strip()) >= 20:
        # 如果文本长度达到或超过 20 个字符，阻止进一步输入
        return "break"


def on_person_entry_enter_pressed(self):
    on_gen_combobox_change(self)


def resource_path(relative_path):
    # 例：加载图像资源
    # image_path = resource_path('resources/image.png')
    """ Get the absolute path to the resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath("..")

    return os.path.join(base_path, relative_path)


def init_gen_frame(self):
    root = self.root
    # ---------- start gen_frame ----------
    # gen_frame = ttk.LabelFrame(root, text="激活产品", borderwidth=10)
    gen_frame = ttk.Frame(root)
    gen_frame.grid(row=0, column=0, padx=5, pady=5, sticky=tk.NS)

    file_names = get_product_option()
    products = [os.path.splitext(item)[0] for item in file_names]
    gen_combobox = ttk.Combobox(gen_frame, values=products, state='readonly', takefocus=True)
    self.gen_combobox = gen_combobox
    gen_combobox.pack(side=tk.LEFT)
    gen_combobox.set('IntelliJ IDEA')
    gen_combobox.bind('<<ComboboxSelected>>', lambda event: on_gen_combobox_change(self))

    gen_btn = ttk.Button(gen_frame, text='生成证书', takefocus=True)
    self.gen_btn = gen_btn
    gen_btn.pack(padx=5, side=tk.LEFT)
    gen_btn.bind('<Button-1>', lambda event: on_gen_btn_click(self))

    install_btn = ttk.Button(gen_frame, text='安装插件', takefocus=True)
    self.install_btn = install_btn
    install_btn.pack(padx=5, side=tk.LEFT)
    install_btn.bind('<Button-1>', lambda event: on_install_btn_click(self))

    uninstall_btn = ttk.Button(gen_frame, text='卸载插件', takefocus=True)
    self.uninstall_btn = uninstall_btn
    uninstall_btn.pack(padx=5, side=tk.LEFT)
    uninstall_btn.bind('<Button-1>', lambda event: on_uninstall_btn_click(self))
    # ---------- end gen_frame ----------


def init_auth_frame(self):
    root = self.root
    # ---------- start auth_frame ----------
    auth_frame = ttk.LabelFrame(root, text="授权信息", borderwidth=10)
    auth_frame.grid(row=1, column=0, padx=5, sticky=tk.NSEW)

    person_label = ttk.Label(auth_frame, text='授权人:')
    person_label.pack(side=tk.LEFT, padx=5)
    person_entry = ttk.Entry(auth_frame)
    self.person_entry = person_entry
    person_entry.pack(side=tk.LEFT)
    person_entry.bind('<Key>', lambda event: on_person_entry_key_press(self))
    person_entry.bind("<Return>", lambda event: on_person_entry_enter_pressed(self))
    person_entry.bind("<KeyRelease>", lambda event: on_person_entry_enter_pressed(self))

    time_label = ttk.Label(auth_frame, text='授权时间:')
    time_label.pack(side=tk.LEFT, padx=5)
    time_str = tk.StringVar()
    time_entry = ttk.Entry(auth_frame, cursor='hand2', textvariable=time_str)
    self.time_entry = time_entry
    time_entry.pack(side=tk.LEFT)
    time_entry.bind('<Button-1>', lambda event: on_time_entry_click(self, time_str))
    # 绑定 <Key> 事件以阻止所有按键输入
    time_entry.bind('<Key>', lambda event: on_disable_key_press(event))
    # 绑定 <Control-v> 和 <Control-Shift-V> 事件以阻止粘贴操作
    time_entry.bind("<Control-v>", lambda event: on_disable_key_press(event))
    time_entry.bind("<Control-Shift-V>", lambda event: on_disable_key_press(event))
    # 绑定 <Button-3> 事件以阻止右键菜单
    time_entry.bind("<Button-3>", lambda event: on_disable_key_press(event))
    # ---------- end auth_frame ----------


def init_sign_frame(self):
    root = self.root
    # ---------- start sign_frame ----------
    sign_frame = ttk.LabelFrame(root, text="签名", borderwidth=10)
    sign_frame.grid(row=2, column=0, padx=5, sticky=tk.NSEW)

    sign_text = tk.Text(sign_frame, width=70, height=10, wrap=tk.WORD, takefocus=False)
    self.sign_text = sign_text
    sign_text.grid(row=0, column=0, sticky=tk.NSEW)
    # sign_text.bind('<Button-1>', lambda event: on_sign_text_click(self))
    sign_text.bind('<Key>', lambda event: on_key_press(event))

    # 创建一个垂直滚动条
    sign_v_scrollbar = ttk.Scrollbar(sign_frame, orient=tk.VERTICAL, command=sign_text.yview)
    sign_v_scrollbar.grid(row=0, column=1, sticky=tk.NS)
    # 将滚动条与 Text 小部件关联
    sign_text.config(yscrollcommand=sign_v_scrollbar.set)

    sign_btn = ttk.Button(sign_frame, text='复制', takefocus=False)
    sign_btn.grid(row=1, column=0, pady=5)
    sign_btn.bind('<Button-1>', lambda event: on_sign_btn_click(self))
    # ---------- end sign_frame ----------


def init_code_frame(self):
    root = self.root
    # ---------- start code_frame ----------
    code_frame = ttk.LabelFrame(root, text="激活码", borderwidth=10)
    code_frame.grid(row=3, column=0, padx=5, sticky=tk.NSEW)

    code_text = tk.Text(code_frame, width=70, height=10, wrap=tk.WORD, takefocus=False)
    self.code_text = code_text
    code_text.grid(row=0, column=0, sticky=tk.NSEW)
    # 创建一个垂直滚动条
    code_v_scrollbar = ttk.Scrollbar(code_frame, orient=tk.VERTICAL, command=code_text.yview)
    code_v_scrollbar.grid(row=0, column=1, sticky=tk.NS)
    # 将滚动条与 Text 小部件关联
    code_text.config(yscrollcommand=code_v_scrollbar.set)
    code_text.bind('<Key>', lambda event: on_key_press(event))

    code_btn = ttk.Button(code_frame, text='复制', takefocus=False)
    code_btn.grid(row=1, column=0, pady=5)
    code_btn.bind('<Button-1>', lambda event: on_code_btn_click(self))
    # ---------- end code_frame ----------


def init_ui(self):
    init_gen_frame(self)
    init_auth_frame(self)
    init_sign_frame(self)
    init_code_frame(self)


class MachineUI:

    def __init__(self, root):
        # 初始化Tkinter窗口
        self.root = root
        # combobox-激活产品
        self.gen_combobox = None
        # btn-生成证书
        self.gen_btn = None
        # entry-授权人
        self.person_entry = None
        # entry-授权时间
        self.time_entry = None
        # text-签名
        self.sign_text = None
        # text-激活码
        self.code_text = None
        # btn-安装插件
        self.install_btn = None
        # btn-卸载插件
        self.uninstall_btn = None

        # 初始化窗口
        root.title('JetBrains注册机')
        init_window(root, 520, 520)

        init_ui(self)

        # 生成默认选中的产品激活码
        on_gen_combobox_change(self)
